Setup

# install.packages("readxl")
library(readxl)
source("https://bioconductor.org/biocLite.R")
Bioconductor version 3.6 (BiocInstaller 1.28.0), ?biocLite for help
A new version of Bioconductor is available after installing the most recent version of R; see http://bioconductor.org/install
biocLite("Biobase")
BioC_mirror: https://bioconductor.org
Using Bioconductor 3.6 (BiocInstaller 1.28.0), R 3.4.3 (2017-11-30).
Installing package(s) 'Biobase'
trying URL 'https://bioconductor.org/packages/3.6/bioc/bin/macosx/el-capitan/contrib/3.4/Biobase_2.38.0.tgz'
Content type 'application/x-gzip' length 2157954 bytes (2.1 MB)
==================================================
downloaded 2.1 MB

Import data

# Version info: R 3.2.3, Biobase 2.30.0, GEOquery 2.40.0, limma 3.26.8
# R scripts generated  Fri Aug 31 00:53:50 EDT 2018


# Server: www.ncbi.nlm.nih.gov
# Query: acc=GSE45120&platform=GPL6244&type=txt&groups=ZR-75-1|ZR-75-30&colors=dfeaf4|f4dfdf&selection=XXXXXX&padj=fdr&logtransform=auto&columns=ID&columns=adj.P.Val&columns=P.Value&columns=t&columns=B&columns=logFC&columns=Gene+symbol&columns=Gene+title&num=250&annot=ncbi

# Unable to generate script analyzing differential expression.
#      Invalid input: at least two groups of samples should be selected.

################################################################
#   Boxplot for selected GEO samples
library(Biobase)
library(GEOquery)

# load series and platform data from GEO

canadas_data <- paste(getwd(),"../..","datasets","canadas_2018/",sep = "/")
gset_canadas0 <- getGEO("GSE45120",destdir = canadas_data)
if (length(gset_canadas0) > 1) idx <- grep("GPL6244", attr(gset, "names")) else idx <- 1
gset_canadas <- gset_canadas0[[idx]]

# # set parameters and draw the plot
# palette(c("#dfeaf4","#f4dfdf", "#AABBCC"))
# dev.new(width=4+dim(gset)[[2]]/5, height=6)
# par(mar=c(2+round(max(nchar(sampleNames(gset)))/2),4,2,1))
# title <- paste ("GSE45120", '/', annotation(gset), " selected samples", sep ='')
# boxplot(exprs(gset), boxwex=0.7, notch=T, main=title, outline=FALSE, las=2)

log2 transform

# log2 transform
exfull <- exprs(gset_canadas)
# remove NAs
gset_canadas_trsf <- gset_canadas
qxfull <- as.numeric(quantile(exfull, c(0., 0.25, 0.5, 0.75, 0.99,1.0), na.rm=T))
LogCfill <- (qxfull[5] > 100) ||
          (qxfull[6]-qxfull[1] > 50 && qxfull[2] > 0) ||
          (qxfull[2] > 0 && qxfull[2] < 1 && qxfull[4] > 1 && qxfull[4] < 2)
# invoke this if log2 normalization isn't already in effect
if (LogCfill) { 
  print("log normalizing")
  exfull[which(exfull <= 0)] <- NaN
  exprs(gset_canadas_trsf) <- log2(exfull) 
  } else if (qxfull[6] > 100) {
  # if the qxfull[6] full is extremely high...
    print("adjusting values")
    # remove extremely high values; since these are log2 fold changes, we don't want anything insanely high
    exfull[which(exfull >= 10)] <- NA
    # also remove extremely low values
    exfull[which(exfull < -10)] <- NA
    exprs(gset_canadas_trsf) <- exfull
}

# remove all rows that are all NA's
gset_canadas_trsf_with_NA <- apply(X = gset_canadas_trsf,
                           MARGIN = 1,
                           function(x) {
                             #length(x[is.na(x)]) > 1
                             length(x[is.na(x)]) == length(x)
                             })
gset_canadas_trsf_with_NA <- gset_canadas_trsf_with_NA[gset_canadas_trsf_with_NA]

Differential expression analysis

The data came from a one-channel microarray

print(gset_canadas$description)

Set design

This is a pretty simple experimental setup: two conditions, one H69M and one H69 (both cell lines). We should consider how lung-cancer inflammation responses vary from EOC, Breast, or Colon cancer responses (as in Li et al 2014 and Chiappinelli et al 2015)

Model fit

We will now fit the model. This doesn’t appear to be log2-normalized, so we will do so here

exprs(gset_canadas_trsf) <- log2(exfull)
lmfit_canadas <- lmFit(gset_canadas_trsf, design_canadas)

DEX genes

Extract necessary statistics and the differentially-expressed genes

alpha <- 0.05
log2fc_lim <- 0.5
canadas_cont_matrix <- makeContrasts(H69M-H69, levels=design_canadas)
lmfit_canadas_fit1 <- contrasts.fit(lmfit_canadas, canadas_cont_matrix)
lmfit_canadas_ebayes <- eBayes(fit = lmfit_canadas_fit1,proportion = alpha)
tT_canadas <- topTable(lmfit_canadas_ebayes, 
                       adjust="fdr", 
                       sort.by="B", number=nrow(gset_canadas_trsf))
# now for the frustrating business of extracting the names of different genes
tT_canadas_gene_names <- sapply(tT_canadas$gene_assignment,
                                function(x) {
                                  a <- unlist(strsplit(x = as.character(x),split = " // ",fixed = TRUE))
                                  a[2]
                                })
tT_canadas$GENE_SYMBOL <- as.character(tT_canadas_gene_names)
tT_canadas_subset <- subset(tT_canadas, select=c("ID","adj.P.Val","P.Value","t","B","logFC","GENE_SYMBOL","AveExpr"))
# # fit1 <- contrasts.fit(fit,cont.matrix)
# fit2 <- contrasts.fit(fit_bycell, cont.matrix)
# # fit1 <- eBayes(fit1, 1)
# fit2 <- eBayes(fit2, 0.01)
# tT <- topTable(fit2, adjust="fdr", sort.by="B", number=250)
# # allT <- topTable(fit2, adjust="none", sort.by="B",number = nrow(fit2))
# tT <- topTable(fit2, adjust="fdr", sort.by="B",number = nrow(fit2))

Volcano plot of the results

plot(tT_canadas_subset$logFC,
     -log10(tT_canadas_subset$adj.P.Val),
     xlab=c("log2FC (H69M-H69)"),
     ylab="adj.P.val",
     pch=20)
tT_canadas_sig_up_h69m <- subset(tT_canadas_subset,adj.P.Val <= alpha & logFC > log2fc_lim)
tT_canadas_sig_down_h69m <- subset(tT_canadas_subset,adj.P.Val <= alpha & logFC < -log2fc_lim)
points(tT_canadas_sig_up_h69m$logFC,
       -log10(tT_canadas_sig_up_h69m$adj.P.Val),
       pch=20,
       col="red")
     #col=as.factor(tT_canadas_subset$adj.P.Val <= alpha & tT_canadas_subset$logFC >= log2fc_lim))
abline(h = -log10(alpha),
       v = log2fc_lim,
       lty=2,
       col="red")

# IFNB has a fold-change in expression of 0.2

Intersect the Canadas set

Canadas et al focused on the 452 most differentially-expressed genes in their analysis (which they obtained from an earlier paper, Canadas et al Clin Cancer Res 2014). The genes are provided in Supplementary Dataset 1, though the exact method to reproduce them is not given.

read_excel_allsheets <- function(filename, tibble = FALSE) {
  # from https://stackoverflow.com/questions/12945687/read-all-worksheets-in-an-excel-workbook-into-an-r-list-with-data-frames 
    sheets <- excel_sheets(filename)
    x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X))
    if(!tibble) x <- lapply(x, as.data.frame)
    names(x) <- sheets
    x
}
canadas_topgenes_sem <- read_excel_allsheets(paste(canadas_data,"41591_2018_116_MOESM3_ESM.xlsx",sep = "/"))
canadas_topgenes_sem_up <- canadas_topgenes_sem$`S2_up_down genes H69M vs H69`$`H69M-UP`
canadas_topgenes_sem_down <- canadas_topgenes_sem$`S2_up_down genes H69M vs H69`$`H69M-DOWN`

Replot the volcano but with the specific genes marked

plot(tT_canadas_subset$logFC,
     -log10(tT_canadas_subset$adj.P.Val),
     xlab=c("log2FC (H69M-H69)"),
     ylab="adj.P.val",
     pch=20)
# tT_canadas_sig_up_h69m <- subset(tT_canadas_subset,adj.P.Val <= alpha & logFC > log2fc_lim)
# tT_canadas_sig_down_h69m <- subset(tT_canadas_subset,adj.P.Val <= alpha & logFC < -log2fc_lim)
points(tT_canadas_sig_up_h69m$logFC,
       -log10(tT_canadas_sig_up_h69m$adj.P.Val),
       pch=20,
       col="red")
     #col=as.factor(tT_canadas_subset$adj.P.Val <= alpha & tT_canadas_subset$logFC >= log2fc_lim))
tT_canadas_sig_up_h69m_in_sem <- subset(tT_canadas_sig_up_h69m,GENE_SYMBOL %in% canadas_topgenes_sem_up)
tT_canadas_sig_up_in_sem <- subset(tT_canadas_subset,GENE_SYMBOL %in% canadas_topgenes_sem_up)
  #subset(tT_canadas_subset,GENE_SYMBOL %in% canadas_topgenes_sem_up | GENE_SYMBOL %in% canadas_topgenes_sem_down)
points(tT_canadas_sig_up_in_sem$logFC,
       -log10(tT_canadas_sig_up_in_sem$adj.P.Val),
       pch=20,
       col="orange")
abline(h = -log10(alpha),
       v = log2fc_lim,
       lty=2,
       col="red")

Heatmap:

# gene annotation
gene_annot <- data.frame(sig_up_h69m = as.factor(tT_canadas_subset$GENE_SYMBOL %in% tT_canadas_sig_up_h69m$GENE_SYMBOL),
                         sig_up_h69m_and_sem = as.factor(tT_canadas_subset$GENE_SYMBOL %in% tT_canadas_sig_up_h69m_in_sem$GENE_SYMBOL),
                         sig_up_sem = as.factor(tT_canadas_subset$GENE_SYMBOL %in% tT_canadas_sig_up_in_sem$GENE_SYMBOL))
rownames(gene_annot) <- tT_canadas_subset$ID
# sample annotation
sample_annot <- gset_canadas_meta
rownames(sample_annot) <- colnames(exprs(gset_canadas_trsf))
# pare down the expression matrix to a desired subset
exp_full <- exprs(gset_canadas_trsf)
gset_canadas_trsf_hmap_sig <- exp_full[rownames(exp_full) %in% rownames(tT_canadas_sig_up_h69m),]
gene_annot_sig <- gene_annot[rownames(gene_annot) %in% rownames(gset_canadas_trsf_hmap_sig),]
# heatmap
pheatmap(gset_canadas_trsf_hmap_sig,
         annotation_col = sample_annot,
         annotation_row = gene_annot_sig,
         show_rownames = FALSE,
         show_colnames = FALSE)

# how many genes are in theirs but not mine?
# genes that are significant to them but not to me
gene_annot_up_sem_uniq <- subset(gene_annot,sig_up_sem == TRUE & sig_up_h69m == FALSE)
# match the canadas-unique probes to genes
gene_annot_up_sem_uniq_genes <- subset(tT_canadas_subset,ID %in% rownames(gene_annot_up_sem_uniq))
gene_annot_up_sem_uniq.n_genes <- unique(gene_annot_up_sem_uniq_genes$GENE_SYMBOL)
print(paste("They report",nrow(gene_annot_up_sem_uniq),"probes representing",length(gene_annot_up_sem_uniq.n_genes),"genes as significantly-upregulated in H69M that I do not"))
[1] "They report 116 probes representing 112 genes as significantly-upregulated in H69M that I do not"
gene_annot_up_mine_uniq <- tT_canadas_sig_up_h69m[!tT_canadas_sig_up_h69m$ID %in% tT_canadas_sig_up_in_sem$ID,]
print(paste("I report",nrow(gene_annot_up_mine_uniq),"probes representing",length(unique(gene_annot_up_mine_uniq$GENE_SYMBOL))," genes as significantly-upregulated in H69M that they do not"))
[1] "I report 45 probes representing 38  genes as significantly-upregulated in H69M that they do not"

Fair concordance between my analysis and theirs.

Save my signatures

write.table(x = tT_canadas_sig_up_in_sem,file = "./tT_canadas_sig_up_in_sem.txt",row.names = TRUE,col.names = TRUE,sep = "\t",quote = FALSE)
write.table(x = tT_canadas_sig_up_h69m,file = "./tT_canadas_sig_up_h69m.txt",row.names = TRUE,col.names = TRUE,sep = "\t",quote = FALSE)
write.table(x = tT_canadas_sig_up_h69m_in_sem,file = "./tT_canadas_sig_up_h69m_in_sem.txt",row.names = TRUE,col.names = TRUE,sep = "\t",quote = FALSE)

GSVA analysis

Setup GSVA

Prepare gsva

prepare_gsva()

Gene set imports

# import some gene sets for GSVA
kegg_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/msigdb_c2.cp.kegg_hsapiens.gmt")
bp_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/msigdb_c5.bp_hsapiens.gmt")
cancer_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/msigdb_c6_hsapiens.gmt")
immune_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/msigdb_c7_hsapiens.gmt")
# from the Li et al oncotarget 2014 paper
interferon_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/custom/msigdb_interferon.gmt")
cytochemokine_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/custom/msigdb_cytochemokine.gmt")
inflammation_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/custom/msigdb_inflammation.gmt")
antigen_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/custom/msigdb_antigen.gmt")
# virus-related terms
virus_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/custom/msigdb_virus.gmt")

# hallmark sets--verified in microarrays
hallmark_genesets <- import_gmt(gmtfile = "../../referenceAnnot/genesets/hg38/msigdb_hallmark_hsapiens.gmt")

Get counts

tT_canadas_sig_up_h69m_expr <- exprs(gset_canadas_trsf)[rownames(exprs(gset_canadas_trsf)) %in% tT_canadas_sig_up_h69m$ID,]
tT_canadas_sig_up_h69m_expr_genes <- sapply(rownames(tT_canadas_sig_up_h69m_expr),
                                            function(x) {
                                              a <- subset(tT_canadas_sig_up_h69m,ID %in% x)
                                              a[,"GENE_SYMBOL"]
                                            })
# remove NA probes
tT_canadas_sig_up_h69m_expr <- tT_canadas_sig_up_h69m_expr[!is.na(tT_canadas_sig_up_h69m_expr_genes),]
tT_canadas_sig_up_h69m_expr_genes <- tT_canadas_sig_up_h69m_expr_genes[!is.na(tT_canadas_sig_up_h69m_expr_genes)]
# ids
tT_canadas_sig_up_h69m_expr_ids <- rownames(tT_canadas_sig_up_h69m_expr)
rownames(tT_canadas_sig_up_h69m_expr) <- tT_canadas_sig_up_h69m_expr_genes

Perform enrichment analysis

# perform GSVA with KEGG
canadas_kegg_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                                                  geneset = kegg_genesets)
# perform GSVA with Cancer pathways
canadas_cancer_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                                                    geneset = cancer_genesets)
# perform GSVA with Immune pathways
canadas_immune_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                                                    geneset = immune_genesets)
# perform GSVA with Immune pathways
canadas_bp_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                                                geneset = bp_genesets)
# perform GSVA with GO terms from Li et al Oncotarget
canadas_interferon_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = interferon_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))
canadas_cytochemokine_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = cytochemokine_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))
canadas_inflammation_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = inflammation_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))
canadas_antigen_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = antigen_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))
# perform GSVA with viral terms
canadas_virus_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = virus_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))
# perform GSVA with hallmark terms
canadas_hallmark_enrich <- gsva_enrich(counts = tT_canadas_sig_up_h69m_expr,
                                 geneset = hallmark_genesets,
                                 ikcdf = "Gaussian",method=c("ssgsea"))

Plots

# heatmaps for pathways
pheatmap(canadas_kegg_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_kegg_enrich),
         show_colnames = FALSE,
         fontsize = 2.5,
         main = "KEGG")

pheatmap(canadas_cancer_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_cancer_enrich),
         show_colnames = FALSE,
         fontsize = 2.5,
         main = "Cancer pathways (general)")

pheatmap(canadas_immune_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_immune_enrich),
         show_colnames = FALSE,
         fontsize = 2.5,
         main = "Immune pathways")

pheatmap(canadas_bp_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_bp_enrich),
         show_colnames = FALSE,
         fontsize = 2.5,
         main = "GO Biological Processes")

# heatmaps for li et al oncotarget signatures
pheatmap(canadas_interferon_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_interferon_enrich),
         show_colnames = FALSE,
         cluster_cols = FALSE,
         fontsize = 2.5,
         main = "Interferon GO Terms")

pheatmap(canadas_cytochemokine_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_cytochemokine_enrich),
         show_colnames = FALSE,
         fontsize = 5.0,
         cluster_cols = FALSE,
         main = "Cytokine/Chemokine GO Terms")

pheatmap(canadas_inflammation_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_inflammation_enrich),
         cluster_rows = FALSE,
         cluster_cols = FALSE,
         show_colnames = FALSE,
         fontsize = 5.0,
         main = "Inflammation GO terms")

pheatmap(canadas_antigen_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_antigen_enrich),
         show_rownames = TRUE,
         fontsize = 4.0,
         show_colnames = FALSE,
         cluster_cols = FALSE,
         main = "Antigen Presentation and Receptor GO Terms")

pheatmap(canadas_virus_enrich,
         annotation_col = gset_canadas_meta,
         labels_row = rownames(canadas_virus_enrich),
         show_rownames = TRUE,
         show_colnames = FALSE,
         cluster_cols = FALSE,
         fontsize_row = 2.5,
         main = "Virus GO Terms")

LS0tCnRpdGxlOiAiU2lnbmF0dXJlIGV4dHJhY3Rpb24gZnJvbSBDYW5hZGFzIGV0IGFsIDIwMTgiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgU2V0dXAKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKbGlicmFyeShyZWFkeGwpCnNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQpiaW9jTGl0ZSgiQmlvYmFzZSIpCmJpb2NMaXRlKCJHRU9xdWVyeSIpCmJpb2NMaXRlKCJsaW1tYSIpCmBgYAoKIyBJbXBvcnQgZGF0YQoKYGBge3J9CiMgVmVyc2lvbiBpbmZvOiBSIDMuMi4zLCBCaW9iYXNlIDIuMzAuMCwgR0VPcXVlcnkgMi40MC4wLCBsaW1tYSAzLjI2LjgKIyBSIHNjcmlwdHMgZ2VuZXJhdGVkICBGcmkgQXVnIDMxIDAwOjUzOjUwIEVEVCAyMDE4CgoKIyBTZXJ2ZXI6IHd3dy5uY2JpLm5sbS5uaWguZ292CiMgUXVlcnk6IGFjYz1HU0U0NTEyMCZwbGF0Zm9ybT1HUEw2MjQ0JnR5cGU9dHh0Jmdyb3Vwcz1aUi03NS0xfFpSLTc1LTMwJmNvbG9ycz1kZmVhZjR8ZjRkZmRmJnNlbGVjdGlvbj1YWFhYWFgmcGFkaj1mZHImbG9ndHJhbnNmb3JtPWF1dG8mY29sdW1ucz1JRCZjb2x1bW5zPWFkai5QLlZhbCZjb2x1bW5zPVAuVmFsdWUmY29sdW1ucz10JmNvbHVtbnM9QiZjb2x1bW5zPWxvZ0ZDJmNvbHVtbnM9R2VuZStzeW1ib2wmY29sdW1ucz1HZW5lK3RpdGxlJm51bT0yNTAmYW5ub3Q9bmNiaQoKIyBVbmFibGUgdG8gZ2VuZXJhdGUgc2NyaXB0IGFuYWx5emluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KIyAgICAgIEludmFsaWQgaW5wdXQ6IGF0IGxlYXN0IHR3byBncm91cHMgb2Ygc2FtcGxlcyBzaG91bGQgYmUgc2VsZWN0ZWQuCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgICBCb3hwbG90IGZvciBzZWxlY3RlZCBHRU8gc2FtcGxlcwpsaWJyYXJ5KEJpb2Jhc2UpCmxpYnJhcnkoR0VPcXVlcnkpCgojIGxvYWQgc2VyaWVzIGFuZCBwbGF0Zm9ybSBkYXRhIGZyb20gR0VPCgpjYW5hZGFzX2RhdGEgPC0gcGFzdGUoZ2V0d2QoKSwiLi4vLi4iLCJkYXRhc2V0cyIsImNhbmFkYXNfMjAxOC8iLHNlcCA9ICIvIikKZ3NldF9jYW5hZGFzMCA8LSBnZXRHRU8oIkdTRTQ1MTIwIixkZXN0ZGlyID0gY2FuYWRhc19kYXRhKQppZiAobGVuZ3RoKGdzZXRfY2FuYWRhczApID4gMSkgaWR4IDwtIGdyZXAoIkdQTDYyNDQiLCBhdHRyKGdzZXQsICJuYW1lcyIpKSBlbHNlIGlkeCA8LSAxCmdzZXRfY2FuYWRhcyA8LSBnc2V0X2NhbmFkYXMwW1tpZHhdXQoKIyAjIHNldCBwYXJhbWV0ZXJzIGFuZCBkcmF3IHRoZSBwbG90CiMgcGFsZXR0ZShjKCIjZGZlYWY0IiwiI2Y0ZGZkZiIsICIjQUFCQkNDIikpCiMgZGV2Lm5ldyh3aWR0aD00K2RpbShnc2V0KVtbMl1dLzUsIGhlaWdodD02KQojIHBhcihtYXI9YygyK3JvdW5kKG1heChuY2hhcihzYW1wbGVOYW1lcyhnc2V0KSkpLzIpLDQsMiwxKSkKIyB0aXRsZSA8LSBwYXN0ZSAoIkdTRTQ1MTIwIiwgJy8nLCBhbm5vdGF0aW9uKGdzZXQpLCAiIHNlbGVjdGVkIHNhbXBsZXMiLCBzZXAgPScnKQojIGJveHBsb3QoZXhwcnMoZ3NldCksIGJveHdleD0wLjcsIG5vdGNoPVQsIG1haW49dGl0bGUsIG91dGxpbmU9RkFMU0UsIGxhcz0yKQoKYGBgCgojIyBsb2cyIHRyYW5zZm9ybQpgYGB7cn0KIyBsb2cyIHRyYW5zZm9ybQpleGZ1bGwgPC0gZXhwcnMoZ3NldF9jYW5hZGFzKQojIHJlbW92ZSBOQXMKZ3NldF9jYW5hZGFzX3Ryc2YgPC0gZ3NldF9jYW5hZGFzCnF4ZnVsbCA8LSBhcy5udW1lcmljKHF1YW50aWxlKGV4ZnVsbCwgYygwLiwgMC4yNSwgMC41LCAwLjc1LCAwLjk5LDEuMCksIG5hLnJtPVQpKQpMb2dDZmlsbCA8LSAocXhmdWxsWzVdID4gMTAwKSB8fAogICAgICAgICAgKHF4ZnVsbFs2XS1xeGZ1bGxbMV0gPiA1MCAmJiBxeGZ1bGxbMl0gPiAwKSB8fAogICAgICAgICAgKHF4ZnVsbFsyXSA+IDAgJiYgcXhmdWxsWzJdIDwgMSAmJiBxeGZ1bGxbNF0gPiAxICYmIHF4ZnVsbFs0XSA8IDIpCiMgaW52b2tlIHRoaXMgaWYgbG9nMiBub3JtYWxpemF0aW9uIGlzbid0IGFscmVhZHkgaW4gZWZmZWN0CmlmIChMb2dDZmlsbCkgeyAKICBwcmludCgibG9nIG5vcm1hbGl6aW5nIikKICBleGZ1bGxbd2hpY2goZXhmdWxsIDw9IDApXSA8LSBOYU4KICBleHBycyhnc2V0X2NhbmFkYXNfdHJzZikgPC0gbG9nMihleGZ1bGwpIAogIH0gZWxzZSBpZiAocXhmdWxsWzZdID4gMTAwKSB7CiAgIyBpZiB0aGUgcXhmdWxsWzZdIGZ1bGwgaXMgZXh0cmVtZWx5IGhpZ2guLi4KICAgIHByaW50KCJhZGp1c3RpbmcgdmFsdWVzIikKICAgICMgcmVtb3ZlIGV4dHJlbWVseSBoaWdoIHZhbHVlczsgc2luY2UgdGhlc2UgYXJlIGxvZzIgZm9sZCBjaGFuZ2VzLCB3ZSBkb24ndCB3YW50IGFueXRoaW5nIGluc2FuZWx5IGhpZ2gKICAgIGV4ZnVsbFt3aGljaChleGZ1bGwgPj0gMTApXSA8LSBOQQogICAgIyBhbHNvIHJlbW92ZSBleHRyZW1lbHkgbG93IHZhbHVlcwogICAgZXhmdWxsW3doaWNoKGV4ZnVsbCA8IC0xMCldIDwtIE5BCiAgICBleHBycyhnc2V0X2NhbmFkYXNfdHJzZikgPC0gZXhmdWxsCn0KCiMgcmVtb3ZlIGFsbCByb3dzIHRoYXQgYXJlIGFsbCBOQSdzCmdzZXRfY2FuYWRhc190cnNmX3dpdGhfTkEgPC0gYXBwbHkoWCA9IGdzZXRfY2FuYWRhc190cnNmLAogICAgICAgICAgICAgICAgICAgICAgICAgICBNQVJHSU4gPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2xlbmd0aCh4W2lzLm5hKHgpXSkgPiAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoKHhbaXMubmEoeCldKSA9PSBsZW5ndGgoeCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KQpnc2V0X2NhbmFkYXNfdHJzZl93aXRoX05BIDwtIGdzZXRfY2FuYWRhc190cnNmX3dpdGhfTkFbZ3NldF9jYW5hZGFzX3Ryc2Zfd2l0aF9OQV0KCmBgYAoKIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyAKClRoZSBkYXRhIGNhbWUgZnJvbSBhIG9uZS1jaGFubmVsIG1pY3JvYXJyYXkKCmBgYHtyIHByaW50X2Rlc2NyaXB0aW9ufQpwcmludChnc2V0X2NhbmFkYXMkZGVzY3JpcHRpb24pCmBgYAoKIyMgU2V0IGRlc2lnbiAKVGhpcyBpcyBhIHByZXR0eSBzaW1wbGUgZXhwZXJpbWVudGFsIHNldHVwOiB0d28gY29uZGl0aW9ucywgb25lIEg2OU0gYW5kIG9uZSBINjkgKGJvdGggY2VsbCBsaW5lcykuIFdlIHNob3VsZCBjb25zaWRlciBob3cgbHVuZy1jYW5jZXIgaW5mbGFtbWF0aW9uIHJlc3BvbnNlcyB2YXJ5IGZyb20gRU9DLCBCcmVhc3QsIG9yIENvbG9uIGNhbmNlciByZXNwb25zZXMgKGFzIGluIExpIGV0IGFsIDIwMTQgYW5kIENoaWFwcGluZWxsaSBldCBhbCAyMDE1KQoKYGBge3Igc2V0X2Rlc2lnbn0KZ3NldF9jYW5hZGFzX21ldGEgPC0gZGF0YS5mcmFtZShjZWxsdHlwZT1nc2V0X2NhbmFkYXMkYGNlbGwgbGluZTpjaDFgKQpyb3duYW1lcyhnc2V0X2NhbmFkYXNfbWV0YSkgPC0gY29sbmFtZXMoZ3NldF9jYW5hZGFzKQpnc2V0X2NhbmFkYXNfdHJzZiRkZXNjcmlwdGlvbiA8LSBnc2V0X2NhbmFkYXNfbWV0YSRjZWxsdHlwZQpkZXNpZ25fY2FuYWRhcyA8LSBtb2RlbC5tYXRyaXgofiAwICsgZGVzY3JpcHRpb24sIGdzZXRfY2FuYWRhc190cnNmKQpjb2xuYW1lcyhkZXNpZ25fY2FuYWRhcykgPC0gbGV2ZWxzKGdzZXRfY2FuYWRhc19tZXRhJGNlbGx0eXBlKQpgYGAKCiMjIE1vZGVsIGZpdAoKV2Ugd2lsbCBub3cgZml0IHRoZSBtb2RlbC4gVGhpcyBkb2Vzbid0IGFwcGVhciB0byBiZSBsb2cyLW5vcm1hbGl6ZWQsIHNvIHdlIHdpbGwgZG8gc28gaGVyZQpgYGB7cn0KZXhwcnMoZ3NldF9jYW5hZGFzX3Ryc2YpIDwtIGxvZzIoZXhmdWxsKQpsbWZpdF9jYW5hZGFzIDwtIGxtRml0KGdzZXRfY2FuYWRhc190cnNmLCBkZXNpZ25fY2FuYWRhcykKYGBgCgojIyBERVggZ2VuZXMKCkV4dHJhY3QgbmVjZXNzYXJ5IHN0YXRpc3RpY3MgYW5kIHRoZSBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMKYGBge3IgZXh0cmFjdF9zdGF0c30KYWxwaGEgPC0gMC4wNQpsb2cyZmNfbGltIDwtIDAuNQpjYW5hZGFzX2NvbnRfbWF0cml4IDwtIG1ha2VDb250cmFzdHMoSDY5TS1INjksIGxldmVscz1kZXNpZ25fY2FuYWRhcykKbG1maXRfY2FuYWRhc19maXQxIDwtIGNvbnRyYXN0cy5maXQobG1maXRfY2FuYWRhcywgY2FuYWRhc19jb250X21hdHJpeCkKbG1maXRfY2FuYWRhc19lYmF5ZXMgPC0gZUJheWVzKGZpdCA9IGxtZml0X2NhbmFkYXNfZml0MSxwcm9wb3J0aW9uID0gYWxwaGEpCnRUX2NhbmFkYXMgPC0gdG9wVGFibGUobG1maXRfY2FuYWRhc19lYmF5ZXMsIAogICAgICAgICAgICAgICAgICAgICAgIGFkanVzdD0iZmRyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgc29ydC5ieT0iQiIsIG51bWJlcj1ucm93KGdzZXRfY2FuYWRhc190cnNmKSkKCiMgbm93IGZvciB0aGUgZnJ1c3RyYXRpbmcgYnVzaW5lc3Mgb2YgZXh0cmFjdGluZyB0aGUgbmFtZXMgb2YgZGlmZmVyZW50IGdlbmVzCnRUX2NhbmFkYXNfZ2VuZV9uYW1lcyA8LSBzYXBwbHkodFRfY2FuYWRhcyRnZW5lX2Fzc2lnbm1lbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYSA8LSB1bmxpc3Qoc3Ryc3BsaXQoeCA9IGFzLmNoYXJhY3Rlcih4KSxzcGxpdCA9ICIgLy8gIixmaXhlZCA9IFRSVUUpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYVsyXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pCnRUX2NhbmFkYXMkR0VORV9TWU1CT0wgPC0gYXMuY2hhcmFjdGVyKHRUX2NhbmFkYXNfZ2VuZV9uYW1lcykKCnRUX2NhbmFkYXNfc3Vic2V0IDwtIHN1YnNldCh0VF9jYW5hZGFzLCBzZWxlY3Q9YygiSUQiLCJhZGouUC5WYWwiLCJQLlZhbHVlIiwidCIsIkIiLCJsb2dGQyIsIkdFTkVfU1lNQk9MIiwiQXZlRXhwciIpKQoKIyAjIGZpdDEgPC0gY29udHJhc3RzLmZpdChmaXQsY29udC5tYXRyaXgpCiMgZml0MiA8LSBjb250cmFzdHMuZml0KGZpdF9ieWNlbGwsIGNvbnQubWF0cml4KQojICMgZml0MSA8LSBlQmF5ZXMoZml0MSwgMSkKIyBmaXQyIDwtIGVCYXllcyhmaXQyLCAwLjAxKQojIHRUIDwtIHRvcFRhYmxlKGZpdDIsIGFkanVzdD0iZmRyIiwgc29ydC5ieT0iQiIsIG51bWJlcj0yNTApCiMgIyBhbGxUIDwtIHRvcFRhYmxlKGZpdDIsIGFkanVzdD0ibm9uZSIsIHNvcnQuYnk9IkIiLG51bWJlciA9IG5yb3coZml0MikpCiMgdFQgPC0gdG9wVGFibGUoZml0MiwgYWRqdXN0PSJmZHIiLCBzb3J0LmJ5PSJCIixudW1iZXIgPSBucm93KGZpdDIpKQoKYGBgCgpWb2xjYW5vIHBsb3Qgb2YgdGhlIHJlc3VsdHMKYGBge3Igdm9sY19wbG90fQpwbG90KHRUX2NhbmFkYXNfc3Vic2V0JGxvZ0ZDLAogICAgIC1sb2cxMCh0VF9jYW5hZGFzX3N1YnNldCRhZGouUC5WYWwpLAogICAgIHhsYWI9YygibG9nMkZDIChINjlNLUg2OSkiKSwKICAgICB5bGFiPSJhZGouUC52YWwiLAogICAgIHBjaD0yMCkKdFRfY2FuYWRhc19zaWdfdXBfaDY5bSA8LSBzdWJzZXQodFRfY2FuYWRhc19zdWJzZXQsYWRqLlAuVmFsIDw9IGFscGhhICYgbG9nRkMgPiBsb2cyZmNfbGltKQp0VF9jYW5hZGFzX3NpZ19kb3duX2g2OW0gPC0gc3Vic2V0KHRUX2NhbmFkYXNfc3Vic2V0LGFkai5QLlZhbCA8PSBhbHBoYSAmIGxvZ0ZDIDwgLWxvZzJmY19saW0pCnBvaW50cyh0VF9jYW5hZGFzX3NpZ191cF9oNjltJGxvZ0ZDLAogICAgICAgLWxvZzEwKHRUX2NhbmFkYXNfc2lnX3VwX2g2OW0kYWRqLlAuVmFsKSwKICAgICAgIHBjaD0yMCwKICAgICAgIGNvbD0icmVkIikKICAgICAjY29sPWFzLmZhY3Rvcih0VF9jYW5hZGFzX3N1YnNldCRhZGouUC5WYWwgPD0gYWxwaGEgJiB0VF9jYW5hZGFzX3N1YnNldCRsb2dGQyA+PSBsb2cyZmNfbGltKSkKYWJsaW5lKGggPSAtbG9nMTAoYWxwaGEpLAogICAgICAgdiA9IGxvZzJmY19saW0sCiAgICAgICBsdHk9MiwKICAgICAgIGNvbD0icmVkIikKCiMgSUZOQiBoYXMgYSBmb2xkLWNoYW5nZSBpbiBleHByZXNzaW9uIG9mIDAuMgpgYGAKCiMjIEludGVyc2VjdCB0aGUgQ2FuYWRhcyBzZXQgCgpDYW5hZGFzIGV0IGFsIGZvY3VzZWQgb24gdGhlIDQ1MiBtb3N0IGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBnZW5lcyBpbiB0aGVpciBhbmFseXNpcyAod2hpY2ggdGhleSBvYnRhaW5lZCBmcm9tIGFuIGVhcmxpZXIgcGFwZXIsIENhbmFkYXMgZXQgYWwgQ2xpbiBDYW5jZXIgUmVzIDIwMTQpLiBUaGUgZ2VuZXMgYXJlIHByb3ZpZGVkIGluIFN1cHBsZW1lbnRhcnkgRGF0YXNldCAxLCB0aG91Z2ggdGhlIGV4YWN0IG1ldGhvZCB0byByZXByb2R1Y2UgdGhlbSBpcyBub3QgZ2l2ZW4uCgpgYGB7ciBpbXBvcnRfc3VwcF9kYXRfMX0KCnJlYWRfZXhjZWxfYWxsc2hlZXRzIDwtIGZ1bmN0aW9uKGZpbGVuYW1lLCB0aWJibGUgPSBGQUxTRSkgewogICMgZnJvbSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMjk0NTY4Ny9yZWFkLWFsbC13b3Jrc2hlZXRzLWluLWFuLWV4Y2VsLXdvcmtib29rLWludG8tYW4tci1saXN0LXdpdGgtZGF0YS1mcmFtZXMgCiAgICBzaGVldHMgPC0gZXhjZWxfc2hlZXRzKGZpbGVuYW1lKQogICAgeCA8LSBsYXBwbHkoc2hlZXRzLCBmdW5jdGlvbihYKSByZWFkeGw6OnJlYWRfZXhjZWwoZmlsZW5hbWUsIHNoZWV0ID0gWCkpCiAgICBpZighdGliYmxlKSB4IDwtIGxhcHBseSh4LCBhcy5kYXRhLmZyYW1lKQogICAgbmFtZXMoeCkgPC0gc2hlZXRzCiAgICB4Cn0KCmNhbmFkYXNfdG9wZ2VuZXNfc2VtIDwtIHJlYWRfZXhjZWxfYWxsc2hlZXRzKHBhc3RlKGNhbmFkYXNfZGF0YSwiNDE1OTFfMjAxOF8xMTZfTU9FU00zX0VTTS54bHN4IixzZXAgPSAiLyIpKQoKY2FuYWRhc190b3BnZW5lc19zZW1fdXAgPC0gY2FuYWRhc190b3BnZW5lc19zZW0kYFMyX3VwX2Rvd24gZ2VuZXMgSDY5TSB2cyBINjlgJGBINjlNLVVQYApjYW5hZGFzX3RvcGdlbmVzX3NlbV9kb3duIDwtIGNhbmFkYXNfdG9wZ2VuZXNfc2VtJGBTMl91cF9kb3duIGdlbmVzIEg2OU0gdnMgSDY5YCRgSDY5TS1ET1dOYAoKYGBgCgpSZXBsb3QgdGhlIHZvbGNhbm8gYnV0IHdpdGggdGhlIHNwZWNpZmljIGdlbmVzIG1hcmtlZApgYGB7cn0KcGxvdCh0VF9jYW5hZGFzX3N1YnNldCRsb2dGQywKICAgICAtbG9nMTAodFRfY2FuYWRhc19zdWJzZXQkYWRqLlAuVmFsKSwKICAgICB4bGFiPWMoImxvZzJGQyAoSDY5TS1INjkpIiksCiAgICAgeWxhYj0iYWRqLlAudmFsIiwKICAgICBwY2g9MjApCiMgdFRfY2FuYWRhc19zaWdfdXBfaDY5bSA8LSBzdWJzZXQodFRfY2FuYWRhc19zdWJzZXQsYWRqLlAuVmFsIDw9IGFscGhhICYgbG9nRkMgPiBsb2cyZmNfbGltKQojIHRUX2NhbmFkYXNfc2lnX2Rvd25faDY5bSA8LSBzdWJzZXQodFRfY2FuYWRhc19zdWJzZXQsYWRqLlAuVmFsIDw9IGFscGhhICYgbG9nRkMgPCAtbG9nMmZjX2xpbSkKcG9pbnRzKHRUX2NhbmFkYXNfc2lnX3VwX2g2OW0kbG9nRkMsCiAgICAgICAtbG9nMTAodFRfY2FuYWRhc19zaWdfdXBfaDY5bSRhZGouUC5WYWwpLAogICAgICAgcGNoPTIwLAogICAgICAgY29sPSJyZWQiKQogICAgICNjb2w9YXMuZmFjdG9yKHRUX2NhbmFkYXNfc3Vic2V0JGFkai5QLlZhbCA8PSBhbHBoYSAmIHRUX2NhbmFkYXNfc3Vic2V0JGxvZ0ZDID49IGxvZzJmY19saW0pKQoKdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9pbl9zZW0gPC0gc3Vic2V0KHRUX2NhbmFkYXNfc2lnX3VwX2g2OW0sR0VORV9TWU1CT0wgJWluJSBjYW5hZGFzX3RvcGdlbmVzX3NlbV91cCkKdFRfY2FuYWRhc19zaWdfdXBfaW5fc2VtIDwtIHN1YnNldCh0VF9jYW5hZGFzX3N1YnNldCxHRU5FX1NZTUJPTCAlaW4lIGNhbmFkYXNfdG9wZ2VuZXNfc2VtX3VwKQogICNzdWJzZXQodFRfY2FuYWRhc19zdWJzZXQsR0VORV9TWU1CT0wgJWluJSBjYW5hZGFzX3RvcGdlbmVzX3NlbV91cCB8IEdFTkVfU1lNQk9MICVpbiUgY2FuYWRhc190b3BnZW5lc19zZW1fZG93bikKCnBvaW50cyh0VF9jYW5hZGFzX3NpZ191cF9pbl9zZW0kbG9nRkMsCiAgICAgICAtbG9nMTAodFRfY2FuYWRhc19zaWdfdXBfaW5fc2VtJGFkai5QLlZhbCksCiAgICAgICBwY2g9MjAsCiAgICAgICBjb2w9Im9yYW5nZSIpCgoKYWJsaW5lKGggPSAtbG9nMTAoYWxwaGEpLAogICAgICAgdiA9IGxvZzJmY19saW0sCiAgICAgICBsdHk9MiwKICAgICAgIGNvbD0icmVkIikKCmBgYAoKSGVhdG1hcDoKCmBgYHtyIGhtYXB9CgojIGdlbmUgYW5ub3RhdGlvbgpnZW5lX2Fubm90IDwtIGRhdGEuZnJhbWUoc2lnX3VwX2g2OW0gPSBhcy5mYWN0b3IodFRfY2FuYWRhc19zdWJzZXQkR0VORV9TWU1CT0wgJWluJSB0VF9jYW5hZGFzX3NpZ191cF9oNjltJEdFTkVfU1lNQk9MKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ191cF9oNjltX2FuZF9zZW0gPSBhcy5mYWN0b3IodFRfY2FuYWRhc19zdWJzZXQkR0VORV9TWU1CT0wgJWluJSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2luX3NlbSRHRU5FX1NZTUJPTCksCiAgICAgICAgICAgICAgICAgICAgICAgICBzaWdfdXBfc2VtID0gYXMuZmFjdG9yKHRUX2NhbmFkYXNfc3Vic2V0JEdFTkVfU1lNQk9MICVpbiUgdFRfY2FuYWRhc19zaWdfdXBfaW5fc2VtJEdFTkVfU1lNQk9MKSkKCnJvd25hbWVzKGdlbmVfYW5ub3QpIDwtIHRUX2NhbmFkYXNfc3Vic2V0JElECgojIHNhbXBsZSBhbm5vdGF0aW9uCnNhbXBsZV9hbm5vdCA8LSBnc2V0X2NhbmFkYXNfbWV0YQpyb3duYW1lcyhzYW1wbGVfYW5ub3QpIDwtIGNvbG5hbWVzKGV4cHJzKGdzZXRfY2FuYWRhc190cnNmKSkKCiMgcGFyZSBkb3duIHRoZSBleHByZXNzaW9uIG1hdHJpeCB0byBhIGRlc2lyZWQgc3Vic2V0CmV4cF9mdWxsIDwtIGV4cHJzKGdzZXRfY2FuYWRhc190cnNmKQpnc2V0X2NhbmFkYXNfdHJzZl9obWFwX3NpZyA8LSBleHBfZnVsbFtyb3duYW1lcyhleHBfZnVsbCkgJWluJSByb3duYW1lcyh0VF9jYW5hZGFzX3NpZ191cF9oNjltKSxdCmdlbmVfYW5ub3Rfc2lnIDwtIGdlbmVfYW5ub3Rbcm93bmFtZXMoZ2VuZV9hbm5vdCkgJWluJSByb3duYW1lcyhnc2V0X2NhbmFkYXNfdHJzZl9obWFwX3NpZyksXQoKIyBoZWF0bWFwCnBoZWF0bWFwKGdzZXRfY2FuYWRhc190cnNmX2htYXBfc2lnLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHNhbXBsZV9hbm5vdCwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBnZW5lX2Fubm90X3NpZywKICAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLAogICAgICAgICBzaG93X2NvbG5hbWVzID0gRkFMU0UpCgojIGhvdyBtYW55IGdlbmVzIGFyZSBpbiB0aGVpcnMgYnV0IG5vdCBtaW5lPwojIGdlbmVzIHRoYXQgYXJlIHNpZ25pZmljYW50IHRvIHRoZW0gYnV0IG5vdCB0byBtZQoKZ2VuZV9hbm5vdF91cF9zZW1fdW5pcSA8LSBzdWJzZXQoZ2VuZV9hbm5vdCxzaWdfdXBfc2VtID09IFRSVUUgJiBzaWdfdXBfaDY5bSA9PSBGQUxTRSkKIyBtYXRjaCB0aGUgY2FuYWRhcy11bmlxdWUgcHJvYmVzIHRvIGdlbmVzCmdlbmVfYW5ub3RfdXBfc2VtX3VuaXFfZ2VuZXMgPC0gc3Vic2V0KHRUX2NhbmFkYXNfc3Vic2V0LElEICVpbiUgcm93bmFtZXMoZ2VuZV9hbm5vdF91cF9zZW1fdW5pcSkpCmdlbmVfYW5ub3RfdXBfc2VtX3VuaXEubl9nZW5lcyA8LSB1bmlxdWUoZ2VuZV9hbm5vdF91cF9zZW1fdW5pcV9nZW5lcyRHRU5FX1NZTUJPTCkKcHJpbnQocGFzdGUoIlRoZXkgcmVwb3J0Iixucm93KGdlbmVfYW5ub3RfdXBfc2VtX3VuaXEpLCJwcm9iZXMgcmVwcmVzZW50aW5nIixsZW5ndGgoZ2VuZV9hbm5vdF91cF9zZW1fdW5pcS5uX2dlbmVzKSwiZ2VuZXMgYXMgc2lnbmlmaWNhbnRseS11cHJlZ3VsYXRlZCBpbiBINjlNIHRoYXQgSSBkbyBub3QiKSkKCmdlbmVfYW5ub3RfdXBfbWluZV91bmlxIDwtIHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1bIXRUX2NhbmFkYXNfc2lnX3VwX2g2OW0kSUQgJWluJSB0VF9jYW5hZGFzX3NpZ191cF9pbl9zZW0kSUQsXQoKcHJpbnQocGFzdGUoIkkgcmVwb3J0Iixucm93KGdlbmVfYW5ub3RfdXBfbWluZV91bmlxKSwicHJvYmVzIHJlcHJlc2VudGluZyIsbGVuZ3RoKHVuaXF1ZShnZW5lX2Fubm90X3VwX21pbmVfdW5pcSRHRU5FX1NZTUJPTCkpLCIgZ2VuZXMgYXMgc2lnbmlmaWNhbnRseS11cHJlZ3VsYXRlZCBpbiBINjlNIHRoYXQgdGhleSBkbyBub3QiKSkKCmBgYAoKRmFpciBjb25jb3JkYW5jZSBiZXR3ZWVuIG15IGFuYWx5c2lzIGFuZCB0aGVpcnMuIAoKU2F2ZSBteSBzaWduYXR1cmVzCmBgYHtyfQp3cml0ZS50YWJsZSh4ID0gdFRfY2FuYWRhc19zaWdfdXBfaW5fc2VtLGZpbGUgPSAiLi90VF9jYW5hZGFzX3NpZ191cF9pbl9zZW0udHh0Iixyb3cubmFtZXMgPSBUUlVFLGNvbC5uYW1lcyA9IFRSVUUsc2VwID0gIlx0IixxdW90ZSA9IEZBTFNFKQp3cml0ZS50YWJsZSh4ID0gdFRfY2FuYWRhc19zaWdfdXBfaDY5bSxmaWxlID0gIi4vdFRfY2FuYWRhc19zaWdfdXBfaDY5bS50eHQiLHJvdy5uYW1lcyA9IFRSVUUsY29sLm5hbWVzID0gVFJVRSxzZXAgPSAiXHQiLHF1b3RlID0gRkFMU0UpCndyaXRlLnRhYmxlKHggPSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2luX3NlbSxmaWxlID0gIi4vdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9pbl9zZW0udHh0Iixyb3cubmFtZXMgPSBUUlVFLGNvbC5uYW1lcyA9IFRSVUUsc2VwID0gIlx0IixxdW90ZSA9IEZBTFNFKQpgYGAKCgoKIyBHU1ZBIGFuYWx5c2lzCgojIyBTZXR1cCBHU1ZBClByZXBhcmUgZ3N2YQpgYGB7cn0KcHJlcGFyZV9nc3ZhKCkKYGBgCgpHZW5lIHNldCBpbXBvcnRzCmBgYHtyfQojIGltcG9ydCBzb21lIGdlbmUgc2V0cyBmb3IgR1NWQQprZWdnX2dlbmVzZXRzIDwtIGltcG9ydF9nbXQoZ210ZmlsZSA9ICIuLi8uLi9yZWZlcmVuY2VBbm5vdC9nZW5lc2V0cy9oZzM4L21zaWdkYl9jMi5jcC5rZWdnX2hzYXBpZW5zLmdtdCIpCmJwX2dlbmVzZXRzIDwtIGltcG9ydF9nbXQoZ210ZmlsZSA9ICIuLi8uLi9yZWZlcmVuY2VBbm5vdC9nZW5lc2V0cy9oZzM4L21zaWdkYl9jNS5icF9oc2FwaWVucy5nbXQiKQpjYW5jZXJfZ2VuZXNldHMgPC0gaW1wb3J0X2dtdChnbXRmaWxlID0gIi4uLy4uL3JlZmVyZW5jZUFubm90L2dlbmVzZXRzL2hnMzgvbXNpZ2RiX2M2X2hzYXBpZW5zLmdtdCIpCmltbXVuZV9nZW5lc2V0cyA8LSBpbXBvcnRfZ210KGdtdGZpbGUgPSAiLi4vLi4vcmVmZXJlbmNlQW5ub3QvZ2VuZXNldHMvaGczOC9tc2lnZGJfYzdfaHNhcGllbnMuZ210IikKIyBmcm9tIHRoZSBMaSBldCBhbCBvbmNvdGFyZ2V0IDIwMTQgcGFwZXIKaW50ZXJmZXJvbl9nZW5lc2V0cyA8LSBpbXBvcnRfZ210KGdtdGZpbGUgPSAiLi4vLi4vcmVmZXJlbmNlQW5ub3QvZ2VuZXNldHMvaGczOC9jdXN0b20vbXNpZ2RiX2ludGVyZmVyb24uZ210IikKY3l0b2NoZW1va2luZV9nZW5lc2V0cyA8LSBpbXBvcnRfZ210KGdtdGZpbGUgPSAiLi4vLi4vcmVmZXJlbmNlQW5ub3QvZ2VuZXNldHMvaGczOC9jdXN0b20vbXNpZ2RiX2N5dG9jaGVtb2tpbmUuZ210IikKaW5mbGFtbWF0aW9uX2dlbmVzZXRzIDwtIGltcG9ydF9nbXQoZ210ZmlsZSA9ICIuLi8uLi9yZWZlcmVuY2VBbm5vdC9nZW5lc2V0cy9oZzM4L2N1c3RvbS9tc2lnZGJfaW5mbGFtbWF0aW9uLmdtdCIpCmFudGlnZW5fZ2VuZXNldHMgPC0gaW1wb3J0X2dtdChnbXRmaWxlID0gIi4uLy4uL3JlZmVyZW5jZUFubm90L2dlbmVzZXRzL2hnMzgvY3VzdG9tL21zaWdkYl9hbnRpZ2VuLmdtdCIpCiMgdmlydXMtcmVsYXRlZCB0ZXJtcwp2aXJ1c19nZW5lc2V0cyA8LSBpbXBvcnRfZ210KGdtdGZpbGUgPSAiLi4vLi4vcmVmZXJlbmNlQW5ub3QvZ2VuZXNldHMvaGczOC9jdXN0b20vbXNpZ2RiX3ZpcnVzLmdtdCIpCgojIGhhbGxtYXJrIHNldHMtLXZlcmlmaWVkIGluIG1pY3JvYXJyYXlzCmhhbGxtYXJrX2dlbmVzZXRzIDwtIGltcG9ydF9nbXQoZ210ZmlsZSA9ICIuLi8uLi9yZWZlcmVuY2VBbm5vdC9nZW5lc2V0cy9oZzM4L21zaWdkYl9oYWxsbWFya19oc2FwaWVucy5nbXQiKQpgYGAKCkdldCBjb3VudHMKYGBge3J9Cgp0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIgPC0gZXhwcnMoZ3NldF9jYW5hZGFzX3Ryc2YpW3Jvd25hbWVzKGV4cHJzKGdzZXRfY2FuYWRhc190cnNmKSkgJWluJSB0VF9jYW5hZGFzX3NpZ191cF9oNjltJElELF0KdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByX2dlbmVzIDwtIHNhcHBseShyb3duYW1lcyh0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGEgPC0gc3Vic2V0KHRUX2NhbmFkYXNfc2lnX3VwX2g2OW0sSUQgJWluJSB4KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYVssIkdFTkVfU1lNQk9MIl0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KQojIHJlbW92ZSBOQSBwcm9iZXMKdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByIDwtIHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwclshaXMubmEodFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByX2dlbmVzKSxdCnRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwcl9nZW5lcyA8LSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHJfZ2VuZXNbIWlzLm5hKHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwcl9nZW5lcyldCiMgaWRzCnRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwcl9pZHMgPC0gcm93bmFtZXModFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByKQpyb3duYW1lcyh0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIpIDwtIHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwcl9nZW5lcwoKYGBgCgoKIyMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzCgpgYGB7cn0KCiMgcGVyZm9ybSBHU1ZBIHdpdGggS0VHRwpjYW5hZGFzX2tlZ2dfZW5yaWNoIDwtIGdzdmFfZW5yaWNoKGNvdW50cyA9IHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXNldCA9IGtlZ2dfZ2VuZXNldHMpCiMgcGVyZm9ybSBHU1ZBIHdpdGggQ2FuY2VyIHBhdGh3YXlzCmNhbmFkYXNfY2FuY2VyX2VucmljaCA8LSBnc3ZhX2VucmljaChjb3VudHMgPSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXNldCA9IGNhbmNlcl9nZW5lc2V0cykKIyBwZXJmb3JtIEdTVkEgd2l0aCBJbW11bmUgcGF0aHdheXMKY2FuYWRhc19pbW11bmVfZW5yaWNoIDwtIGdzdmFfZW5yaWNoKGNvdW50cyA9IHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lc2V0ID0gaW1tdW5lX2dlbmVzZXRzKQojIHBlcmZvcm0gR1NWQSB3aXRoIEltbXVuZSBwYXRod2F5cwpjYW5hZGFzX2JwX2VucmljaCA8LSBnc3ZhX2VucmljaChjb3VudHMgPSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lc2V0ID0gYnBfZ2VuZXNldHMpCgojIHBlcmZvcm0gR1NWQSB3aXRoIEdPIHRlcm1zIGZyb20gTGkgZXQgYWwgT25jb3RhcmdldApjYW5hZGFzX2ludGVyZmVyb25fZW5yaWNoIDwtIGdzdmFfZW5yaWNoKGNvdW50cyA9IHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXNldCA9IGludGVyZmVyb25fZ2VuZXNldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlrY2RmID0gIkdhdXNzaWFuIixtZXRob2Q9Yygic3Nnc2VhIikpCmNhbmFkYXNfY3l0b2NoZW1va2luZV9lbnJpY2ggPC0gZ3N2YV9lbnJpY2goY291bnRzID0gdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lc2V0ID0gY3l0b2NoZW1va2luZV9nZW5lc2V0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWtjZGYgPSAiR2F1c3NpYW4iLG1ldGhvZD1jKCJzc2dzZWEiKSkKY2FuYWRhc19pbmZsYW1tYXRpb25fZW5yaWNoIDwtIGdzdmFfZW5yaWNoKGNvdW50cyA9IHRUX2NhbmFkYXNfc2lnX3VwX2g2OW1fZXhwciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXNldCA9IGluZmxhbW1hdGlvbl9nZW5lc2V0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWtjZGYgPSAiR2F1c3NpYW4iLG1ldGhvZD1jKCJzc2dzZWEiKSkKY2FuYWRhc19hbnRpZ2VuX2VucmljaCA8LSBnc3ZhX2VucmljaChjb3VudHMgPSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzZXQgPSBhbnRpZ2VuX2dlbmVzZXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpa2NkZiA9ICJHYXVzc2lhbiIsbWV0aG9kPWMoInNzZ3NlYSIpKQoKIyBwZXJmb3JtIEdTVkEgd2l0aCB2aXJhbCB0ZXJtcwpjYW5hZGFzX3ZpcnVzX2VucmljaCA8LSBnc3ZhX2VucmljaChjb3VudHMgPSB0VF9jYW5hZGFzX3NpZ191cF9oNjltX2V4cHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzZXQgPSB2aXJ1c19nZW5lc2V0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWtjZGYgPSAiR2F1c3NpYW4iLG1ldGhvZD1jKCJzc2dzZWEiKSkKCiMgcGVyZm9ybSBHU1ZBIHdpdGggaGFsbG1hcmsgdGVybXMKY2FuYWRhc19oYWxsbWFya19lbnJpY2ggPC0gZ3N2YV9lbnJpY2goY291bnRzID0gdFRfY2FuYWRhc19zaWdfdXBfaDY5bV9leHByLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lc2V0ID0gaGFsbG1hcmtfZ2VuZXNldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlrY2RmID0gIkdhdXNzaWFuIixtZXRob2Q9Yygic3Nnc2VhIikpCgoKYGBgCgoKIyMgUGxvdHMKCmBgYHtyfQojIGhlYXRtYXBzIGZvciBwYXRod2F5cwpwaGVhdG1hcChjYW5hZGFzX2tlZ2dfZW5yaWNoLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGdzZXRfY2FuYWRhc19tZXRhLAogICAgICAgICBsYWJlbHNfcm93ID0gcm93bmFtZXMoY2FuYWRhc19rZWdnX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgZm9udHNpemUgPSAyLjUsCiAgICAgICAgIG1haW4gPSAiS0VHRyIpCnBoZWF0bWFwKGNhbmFkYXNfY2FuY2VyX2VucmljaCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBnc2V0X2NhbmFkYXNfbWV0YSwKICAgICAgICAgbGFiZWxzX3JvdyA9IHJvd25hbWVzKGNhbmFkYXNfY2FuY2VyX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgZm9udHNpemUgPSAyLjUsCiAgICAgICAgIG1haW4gPSAiQ2FuY2VyIHBhdGh3YXlzIChnZW5lcmFsKSIpCnBoZWF0bWFwKGNhbmFkYXNfaW1tdW5lX2VucmljaCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBnc2V0X2NhbmFkYXNfbWV0YSwKICAgICAgICAgbGFiZWxzX3JvdyA9IHJvd25hbWVzKGNhbmFkYXNfaW1tdW5lX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgZm9udHNpemUgPSAyLjUsCiAgICAgICAgIG1haW4gPSAiSW1tdW5lIHBhdGh3YXlzIikKcGhlYXRtYXAoY2FuYWRhc19icF9lbnJpY2gsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gZ3NldF9jYW5hZGFzX21ldGEsCiAgICAgICAgIGxhYmVsc19yb3cgPSByb3duYW1lcyhjYW5hZGFzX2JwX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgZm9udHNpemUgPSAyLjUsCiAgICAgICAgIG1haW4gPSAiR08gQmlvbG9naWNhbCBQcm9jZXNzZXMiKQoKCgpgYGAKCgoKYGBge3J9CiMgaGVhdG1hcHMgZm9yIGxpIGV0IGFsIG9uY290YXJnZXQgc2lnbmF0dXJlcwpwaGVhdG1hcChjYW5hZGFzX2ludGVyZmVyb25fZW5yaWNoLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGdzZXRfY2FuYWRhc19tZXRhLAogICAgICAgICBsYWJlbHNfcm93ID0gcm93bmFtZXMoY2FuYWRhc19pbnRlcmZlcm9uX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgICAgIGZvbnRzaXplID0gMi41LAogICAgICAgICBtYWluID0gIkludGVyZmVyb24gR08gVGVybXMiKQpwaGVhdG1hcChjYW5hZGFzX2N5dG9jaGVtb2tpbmVfZW5yaWNoLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGdzZXRfY2FuYWRhc19tZXRhLAogICAgICAgICBsYWJlbHNfcm93ID0gcm93bmFtZXMoY2FuYWRhc19jeXRvY2hlbW9raW5lX2VucmljaCksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgZm9udHNpemUgPSA1LjAsCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IEZBTFNFLAogICAgICAgICBtYWluID0gIkN5dG9raW5lL0NoZW1va2luZSBHTyBUZXJtcyIpCnBoZWF0bWFwKGNhbmFkYXNfaW5mbGFtbWF0aW9uX2VucmljaCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBnc2V0X2NhbmFkYXNfbWV0YSwKICAgICAgICAgbGFiZWxzX3JvdyA9IHJvd25hbWVzKGNhbmFkYXNfaW5mbGFtbWF0aW9uX2VucmljaCksCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogICAgICAgICBmb250c2l6ZSA9IDUuMCwKICAgICAgICAgbWFpbiA9ICJJbmZsYW1tYXRpb24gR08gdGVybXMiKQpwaGVhdG1hcChjYW5hZGFzX2FudGlnZW5fZW5yaWNoLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGdzZXRfY2FuYWRhc19tZXRhLAogICAgICAgICBsYWJlbHNfcm93ID0gcm93bmFtZXMoY2FuYWRhc19hbnRpZ2VuX2VucmljaCksCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLAogICAgICAgICBmb250c2l6ZSA9IDQuMCwKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwKICAgICAgICAgbWFpbiA9ICJBbnRpZ2VuIFByZXNlbnRhdGlvbiBhbmQgUmVjZXB0b3IgR08gVGVybXMiKQpwaGVhdG1hcChjYW5hZGFzX3ZpcnVzX2VucmljaCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBnc2V0X2NhbmFkYXNfbWV0YSwKICAgICAgICAgbGFiZWxzX3JvdyA9IHJvd25hbWVzKGNhbmFkYXNfdmlydXNfZW5yaWNoKSwKICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDIuNSwKICAgICAgICAgbWFpbiA9ICJWaXJ1cyBHTyBUZXJtcyIpCgoKYGBgCgoKCg==